package com.chia.multienty.core.infra.mybatis.method;

import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils;
import com.chia.multienty.core.infra.sharding.tools.ShardingAlgorithmTool;
import com.chia.multienty.core.mybatis.KutaSqlMethod;
import com.chia.multienty.core.mybatis.generator.ShardingInfo;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;

import java.util.Objects;

import static java.util.stream.Collectors.joining;

public class UpdateByIdAndShardingMethod extends AbstractMethod {
    /**
     * @since 3.5.0
     */
    public UpdateByIdAndShardingMethod() {
        super(KutaSqlMethod.UPDATE_BY_ID_AND_SHARDING.getName());
    }

    private static final String SQL_FLAG = " AND `%s`=#{et.%s}";
    public static final String SQL_SEGMENT_CREATE_TIME = " AND `%s` &gt;= #{sharding.start} AND `%s` &lt;= #{sharding.end}";
    @Override
    public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        KutaSqlMethod sqlMethod = KutaSqlMethod.UPDATE_BY_ID_AND_SHARDING;
        final String additional = optlockVersion(tableInfo) + tableInfo.getLogicDeleteSql(true, true);
        String tableName = tableInfo.getTableName();

        ShardingInfo shardingInfo = ShardingAlgorithmTool.getShardingInfo(tableName);
        StringBuilder sb = new StringBuilder();



        if(shardingInfo.getShardingDatabase()) {
            sb.append(String.format(SQL_FLAG, shardingInfo.getDatabaseShardingColumnName(), shardingInfo.getDatabaseShardingProperty()));
        }
        if(shardingInfo.getShardingTable()) {
            if(shardingInfo.getTableShardingColumnName().equals("create_time")) {
                sb.append(
                        String.format(SQL_SEGMENT_CREATE_TIME,
                                shardingInfo.getTableShardingColumnName(),
                                shardingInfo.getTableShardingColumnName()
                        )
                );
            } else {
                sb.append(String.format(SQL_FLAG, shardingInfo.getTableShardingColumnName(), shardingInfo.getTableShardingProperty()));
            }
        }

        String shardingSql = sb.toString();
        String sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(),
                sqlSet(tableInfo.isWithLogicDelete(), false, tableInfo, false, ENTITY, ENTITY_DOT, shardingInfo),
                tableInfo.getKeyColumn(), ENTITY_DOT + tableInfo.getKeyProperty(), shardingSql, additional);
        SqlSource sqlSource = super.createSqlSource(configuration, sql, modelClass);
        return addUpdateMappedStatement(mapperClass, modelClass, methodName, sqlSource);
    }

    protected String sqlSet(boolean logic, boolean ew, TableInfo table, boolean judgeAliasNull, final String alias,
                            final String prefix, ShardingInfo shardingInfo) {
        table.getAllSqlSet(logic, prefix);
        String sqlScript = getAllSqlSet(logic, prefix, table, shardingInfo);
        if (judgeAliasNull) {
            sqlScript = SqlScriptUtils.convertIf(sqlScript, String.format("%s != null", alias), true);
        }
        if (ew) {
            sqlScript += NEWLINE;
            sqlScript += convertIfEwParam(U_WRAPPER_SQL_SET, false);
        }
        sqlScript = SqlScriptUtils.convertSet(sqlScript);
        return sqlScript;
    }

    public String getAllSqlSet(boolean ignoreLogicDelFiled, final String prefix, TableInfo tableInfo, ShardingInfo shardingInfo) {
        final String newPrefix = prefix == null ? EMPTY : prefix;
        return tableInfo.getFieldList().stream()
                .filter(i -> {
                    if(shardingInfo.getShardingDatabase() && shardingInfo.getDatabaseShardingProperty().equals(i.getProperty())) {
                        return false;
                    }
                    if(shardingInfo.getShardingTable() && shardingInfo.getTableShardingProperty().equals(i.getProperty())) {
                        return false;
                    }
                    if (ignoreLogicDelFiled) {
                        return !(tableInfo.isWithLogicDelete() && i.isLogicDelete());
                    }
                    return true;
                }).map(i -> i.getSqlSet(newPrefix)).filter(Objects::nonNull).collect(joining(NEWLINE));
    }
}
